package cn.supermaster.management.core.realm;

import java.util.Set;

import cn.supermaster.management.core.component.LoginManager;
import cn.supermaster.management.system.service.ISystemMenuService;
import cn.supermaster.management.system.service.ISystemRoleService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import cn.supermaster.management.common.domain.SystemUser;
import cn.supermaster.management.common.exception.user.CaptchaException;
import cn.supermaster.management.common.exception.user.RoleBlockedException;
import cn.supermaster.management.common.exception.user.UserBlockedException;
import cn.supermaster.management.common.exception.user.UserNotExistsException;
import cn.supermaster.management.common.exception.user.UserPasswordNotMatchException;
import cn.supermaster.management.common.exception.user.UserPasswordRetryLimitExceedException;
import cn.supermaster.management.common.utils.ShiroUtils;

/**
 * All rights Reserved, Designed By www.super-bytecode.com
 *
 * @ProjectName: supermaster-management
 * @Package: cn.supermaster.management.core.realm
 * @ClassName: UserRealm
 * @Description: [自定义Realm 处理登录 权限]
 * <p> </p>
 * @Author: [Lance Ting]
 * @Date: 2021/1/10 22:23
 * @Version: V1.0
 * @Copyright: 2021 www.super-bytecode.comn Inc. All rights reserved.
 * TODO: 注意,本文件Lance Ting所作,如果转载或使用请标明具体出处!
 **/
public class UserRealm extends AuthorizingRealm {

    private static final Logger logger = LoggerFactory.getLogger(UserRealm.class);

    @Autowired
    private ISystemMenuService menuService;

    @Autowired
    private ISystemRoleService roleService;

    @Autowired
    private LoginManager loginService;

    /**
     * @Description: [授权]
     * <p></P>
     * @Title: doGetAuthorizationInfo
     * @Author: Lance Ting
     * @Date: 2021-01-10
     * @Param: principal
     * @Return: org.apache.shiro.authz.AuthorizationInfo
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        // 获取用户信息
        SystemUser user = ShiroUtils.getSystemUser();
        // 设置权限信息
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        if (user.isAdmin()) {
            // 管理员拥有所有权限
            setAdminAuthorization(authorizationInfo);
        } else {
            // 普通用户拥有所有权限
            setCommonAuthorization(authorizationInfo, user);
        }
        return authorizationInfo;
    }


    /**
     * @Description: [登录认证]
     * <p></P>
     * @Title: doGetAuthenticationInfo
     * @Author: Lance Ting
     * @Date: 2021-01-10
     * @Param: token
     * @Return: org.apache.shiro.authc.AuthenticationInfo
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        String username = userToken.getUsername();
        String password = "";
        if (userToken.getPassword() != null) {
            password = new String(userToken.getPassword());
        }

        // 用户认操作
        SystemUser user = null;
        try {
            user = loginService.login(username, password);
        } catch (Exception e) {
            // 异常处理
            doAuthenticationThrows(e, username);
        }

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
        return info;
    }

    /**
     * @Description: [清理指定用户授权信息缓存]
     * <p></P>
     * @Title: clearCachedAuthorizationInfo
     * @Author: Lance Ting
     * @Date: 2021-01-10
     * @Param: principal
     * @Return: void
     */
    public void clearCachedAuthorizationInfo(Object principal) {
        SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
        this.clearCachedAuthorizationInfo(principals);
    }

    /**
     * @Description: [清理所有用户授权信息缓存]
     * <p></P>
     * @Title: clearAllCachedAuthorizationInfo
     * @Author: Lance Ting
     * @Date: 2021-01-10
     * @Return: void
     */
    public void clearAllCachedAuthorizationInfo() {
        Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
        if (null == cache) return;
        //
        for (Object key : cache.keys()) {
            cache.remove(key);
        }
    }

    /**
     * @Description: [设置超级管理员权限字符标识]
     * <p></P>
     * @Title: setAdminAuthorization
     * @Author: Lance Ting
     * @Date: 2021-01-10
     * @Param: authorizationInfo
     * @Return: void
     */
    private void setAdminAuthorization(SimpleAuthorizationInfo authorizationInfo) {
        authorizationInfo.addRole("admin");
        authorizationInfo.addStringPermission("*:*:*");
    }

    /**
     * @Description: [设置普通用户权限字符标识]
     * <p></P>
     * @Title: setAdminAuthorization
     * @Author: Lance Ting
     * @Date: 2021-01-10
     * @Param: authorizationInfo 认证信息
     * @Param: user 用户信息
     * @Return: void
     */
    private void setCommonAuthorization(SimpleAuthorizationInfo authorizationInfo, SystemUser user) {
        // 角色列表
        Set<String> roles = roleService.selectRoleKeys(user.getUserId());
        // 功能列表
        Set<String> menus = menuService.selectPermsByUserId(user.getUserId());
        // 角色加入AuthorizationInfo认证对象
        authorizationInfo.setRoles(roles);
        // 权限加入AuthorizationInfo认证对象
        authorizationInfo.setStringPermissions(menus);
    }

    /**
     * @Description: [处理认证功能Exception]
     * <p></P>
     * @Title: doAuthenticationThrows
     * @Author: Lance Ting
     * @Date: 2021-01-10
     * @Param: exception  异常对象
     * @Param: username 用户名称
     * @Return: void
     */
    private void doAuthenticationThrows(Exception exception, String username) {
        String message = exception.getMessage();
        if (exception instanceof CaptchaException) {
            // 验证码异常
            throw new AuthenticationException(message, exception);
        } else if (exception instanceof UserNotExistsException) {
            // 账户信息无法找到异常
            throw new UnknownAccountException(message, exception);
        } else if (exception instanceof UserPasswordNotMatchException) {
            // 用户密码错误异常
            throw new IncorrectCredentialsException(message, exception);
        } else if (exception instanceof UserPasswordRetryLimitExceedException) {
            // 用户密码重试最大次数异常
            throw new ExcessiveAttemptsException(message, exception);
        } else if (exception instanceof UserBlockedException) {
            // 用户锁定异常
            throw new LockedAccountException(message, exception);
        } else if (exception instanceof RoleBlockedException) {
            // 用户角色锁定异常
            throw new LockedAccountException(message, exception);
        } else if (exception instanceof Exception) {
            // 其他认证异常
            logger.info("对用户[" + username + "]进行登录验证..验证未通过{}", message);
            throw new AuthenticationException(message, exception);
        }
    }
}
